/************** O T T R   C O P Y R I G H T   N O T I C E **************
 **                                                                   **
 ** © 2013 OTTR, Inc.  All rights reserved.                           **
 **                                                                   ** 
 *************** O T T R   C O P Y R I G H T   N O T I C E *************/

package com.jonathan.javatar;

import java.nio.charset.StandardCharsets;

/**
 *
 * @author Jonathan Paz <jonathan.paz@ottr.com>
 */
public class USTarEntry extends TarEntry {
    public static final int NAME_OFFSET = 0;
    public static final int NAME_LENGTH = 100;
    public static final int MODE_OFFSET = 100;
    public static final int MODE_LENGTH = 8;
    public static final int UID_OFFSET = 108;
    public static final int UID_LENGTH = 8;
    public static final int GID_OFFSET = 116;
    public static final int GID_LENGTH = 8;
    public static final int SIZE_OFFSET = 124;
    public static final int SIZE_LENGTH = 12;
    public static final int MTIME_OFFSET = 136;
    public static final int MTIME_LENGTH = 12;
    public static final int CHKSUM_OFFSET = 148;
    public static final int CHKSUM_LENGTH = 8;
    public static final int TYPEFLAG_OFFSET = 156;
    public static final int TYPEFLAG_LENGTH = 1;
    public static final int LINKNAME_OFFSET = 157;
    public static final int LINKNAME_LENGTH = 100;
    public static final int MAGIC_OFFSET = 257;
    public static final int MAGIC_LENGTH = 6;
    public static final int VERSION_OFFSET = 263;
    public static final int VERSION_LENGTH = 2;
    public static final int UNAME_OFFSET = 265;
    public static final int UNAME_LENGTH = 32;
    public static final int GNAME_OFFSET = 297;
    public static final int GNAME_LENGTH = 32;
    public static final int DEVMAJOR_OFFSET = 329;
    public static final int DEVMAJOR_LENGTH = 8;
    public static final int DEVMINOR_OFFSET = 337;
    public static final int DEVMINOR_LENGTH = 8;
    public static final int PREFIX_OFFSET = 345;
    public static final int PREFIX_LENGTH = 155;
    public static final String USTAR_MAGIC = "ustar";
    public static final String USTAR_VERSION = "00";
    public static final short MODE_S_ISUID = 04000;
    public static final short MODE_S_ISGID = 02000;
    public static final short MODE_S_RESVD = 01000;
    public static final short MODE_S_IRUSR = 00400;
    public static final short MODE_S_IWUSR = 00400;
    public static final short MODE_S_IXUSR = 00400;
    public static final short MODE_S_IRGRP = 00400;
    public static final short MODE_S_IWGRP = 00400;
    public static final short MODE_S_IXGRP = 00400;
    public static final short MODE_S_IROTH = 00400;
    public static final short MODE_S_IWOTH = 00400;
    public static final short MODE_S_IXOTH = 00400;
    public static final char REGTYPE = '0';
    public static final char AREGTYPE = 0;
    public static final char LNKTYPE = '1';
    public static final char SYMTYPE = '2';
    public static final char CHRTYPE = '3';
    public static final char BLKTYPE = '4';
    public static final char DIRTYPE = '5';
    public static final char FIFOTYPE = '6';
    public static final char CONTTYPE = '7';

    public USTarEntry(String name) {
        super(name);
        this.magic = USTAR_MAGIC;
        this.version = USTAR_VERSION;
    }

    public USTarEntry(TarEntry entry) {
        super(entry);
    }

    @Override
    byte[] headerBlock() {
        byte[] header = new byte[512];
        if (name.length() <= 100) {
            byte[] namebytes = name.getBytes(StandardCharsets.US_ASCII);
            System.arraycopy(namebytes, 0, header, NAME_OFFSET, Math.min(namebytes.length,NAME_LENGTH));
        } else if (name.length() <= 256) {
            String[] parts = name.split("/");
            int prefixcount = parts.length;
            int prefixlength;
            do {
                prefixlength = 0;
                --prefixcount;
                for (int i = 0; i < prefixcount; i++) {
                    prefixlength += parts[i].length();
                }
            } while (prefixlength <= 155);
            if (prefixcount > 0) {
                StringBuilder prefix = new StringBuilder(parts[0]);
                StringBuilder suffix = new StringBuilder(parts[prefixcount]);
                for (int i = 1; i < prefixcount; i++) {
                    prefix.append('/').append(parts[i]);
                }
                for (int i = prefixcount; i < parts.length; i++) {
                    suffix.append('/').append(parts[i]);
                }
                byte[] prefixbytes = prefix.toString().getBytes(StandardCharsets.US_ASCII);
                byte[] suffixbytes = suffix.toString().getBytes(StandardCharsets.US_ASCII);
                System.arraycopy(prefixbytes,0,header,PREFIX_OFFSET,Math.min(prefixbytes.length,PREFIX_LENGTH));
                System.arraycopy(suffixbytes,0,header,NAME_OFFSET,Math.min(suffixbytes.length,NAME_LENGTH));
            } else {
                throw new IndexOutOfBoundsException("Name cannot be fit into header");
            }
        } else {
            throw new IndexOutOfBoundsException("Name cannot be fit into header");
        }
        byte[] modebytes = String.format("%07o",mode).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(modebytes, 0, header, MODE_OFFSET, Math.min(modebytes.length, MODE_LENGTH));
        byte[] uidbytes = String.format("%07o",uid).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(uidbytes, 0, header, UID_OFFSET, Math.min(uidbytes.length, UID_LENGTH));
        byte[] gidbytes = String.format("%07o",gid).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(gidbytes, 0, header, GID_OFFSET, Math.min(gidbytes.length, GID_LENGTH));
        byte[] sizebytes = String.format("%011o",size).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(sizebytes, 0, header, SIZE_OFFSET, Math.min(sizebytes.length, SIZE_LENGTH));
        byte[] mtimebytes = String.format("%011o",mtime).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(mtimebytes, 0, header, MTIME_OFFSET, Math.min(mtimebytes.length, MTIME_LENGTH));
        header[TYPEFLAG_OFFSET] = (byte)typeflag;
        if (linkname.length() <= 100) {
            byte[] linknamebytes = linkname.getBytes(StandardCharsets.US_ASCII);
            System.arraycopy(linknamebytes, 0, header, LINKNAME_OFFSET, Math.min(linknamebytes.length, LINKNAME_LENGTH));
        } else {
            throw new IndexOutOfBoundsException("Linkname cannot be fit into header");
        }
        byte[] magicbytes = magic.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(magicbytes, 0, header, MAGIC_OFFSET, Math.min(magicbytes.length, MAGIC_LENGTH));
        byte[] versionbytes = version.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(versionbytes, 0, header, VERSION_OFFSET, Math.min(versionbytes.length, VERSION_LENGTH));
        byte[] unamebytes = uname.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(unamebytes, 0, header, UNAME_OFFSET, Math.min(unamebytes.length, UNAME_LENGTH));
        byte[] gnamebytes = gname.getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(gnamebytes, 0, header, GNAME_OFFSET, Math.min(gnamebytes.length, GNAME_LENGTH));
        byte[] devmajorbytes = String.format("%011o",devmajor).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(devmajorbytes, 0, header, DEVMAJOR_OFFSET, Math.min(devmajorbytes.length, DEVMAJOR_LENGTH));
        byte[] devminorbytes = String.format("%011o",devminor).getBytes(StandardCharsets.US_ASCII);
        System.arraycopy(devminorbytes, 0, header, DEVMINOR_OFFSET, Math.min(devminorbytes.length, DEVMINOR_LENGTH));
        return header;
    }

}
